Skip to content

Conversation

ashwinrava
Copy link
Member

This PR implements the routing logic below. Changes here: a8c41f2

  • Update /limits to return utilization data so that we don't need to refetch it
  • Call limits with a standard amount to maximize cache hits
  • Use data to determine Across/CCTP bridge strategy
image

Copy link

linear bot commented Sep 30, 2025

Copy link

vercel bot commented Sep 30, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
app-frontend-v3 Ready Ready Preview Comment Oct 9, 2025 8:53pm
sepolia-frontend-v3 Ready Ready Preview Comment Oct 9, 2025 8:53pm

@ashwinrava ashwinrava changed the base branch from master to mguevara/acx-4479-add-cctp-bridge-strategy September 30, 2025 12:02
Copy link
Contributor

@dohaki dohaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some remarks! Also some inline comments seem to be incorrect/outdated

const isLargeDeposit = depositAmountUsd > 1_000_000; // 1M USD

// Check if eligible for Fast CCTP (Polygon, BSC, Solana) and deposit > 10K USD
const fastCctpChains = [CHAIN_IDs.POLYGON, CHAIN_IDs.BSC, CHAIN_IDs.SOLANA];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be part of the CCTP bridge strategy?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for this use case we are already exporting CCTP_FILL_TIME_ESTIMATES from the strategy. Maybe we can use that combined with a threshold.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast CCTP chains can be taken from here. Chains with "seconds" are considered fast

@ashwinrava ashwinrava marked this pull request as draft September 30, 2025 14:18
@ashwinrava ashwinrava force-pushed the ashwin/acx-4481-implement-acrossnative-routing-logic branch from a8c41f2 to 671465d Compare September 30, 2025 20:11
@ashwinrava ashwinrava changed the base branch from mguevara/acx-4479-add-cctp-bridge-strategy to master September 30, 2025 20:12
@ashwinrava ashwinrava marked this pull request as ready for review September 30, 2025 20:29
@ashwinrava ashwinrava changed the base branch from master to feat/mint-burn-bridge-routing September 30, 2025 21:51
Comment on lines +65 to +90
if (bridgeStrategyData.isUtilizationHigh) {
return getCctpBridgeStrategy();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when utilization is high CCTP strategy should be used only for USDC, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we won't ever reach this line unless we're bridging USDC to USDC. See line 65 for the early exit clause.


import { CrossSwap, CrossSwapQuotes, Token } from "../_dexes/types";
import { AppFee, CrossSwapType } from "../_dexes/utils";
import { Logger } from "@across-protocol/sdk/dist/types/relayFeeCalculator";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we have some utils in api/_logger.ts. Can we use getLogger from there?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I checked and that's also using the same one: https://github.com/across-protocol/frontend/blob/master/api/_logger.ts#L8

In this file, I'm only using the type Logger, not the actual logger instance.

Copy link
Contributor

@melisaguevara melisaguevara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking nice. Left some comments/questions.

return getAcrossBridgeStrategy();
} else {
// Use OFT bridge if not CCTP
return getCctpBridgeStrategy();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for all these checks, should we check if the strategy we want to return is part of the supportedBridgeStrategies list? That way we avoid returning here a strategy that was previously filtered out.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored it a bit to achieve this. It will help us when we add the OFT strategy as well.

Comment on lines 77 to 79
const isFastCctpChain =
fastCctpChains.includes(inputToken.chainId) ||
fastCctpChains.includes(outputToken.chainId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think only the origin chain matters here.

const utilizationThreshold = sdk.utils.fixedPointAdjustment.mul(80).div(100); // 80%

// Calculate current utilization percentage
const currentUtilization = _utilizedReserves
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think utilizedReserves can be negative, would that have any impact in this calculation?
I see in other places we floored it to zero when that happens. See for example: https://github.com/across-protocol/frontend/blob/master/api/_utils.ts#L2709

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

});

// Safely return undefined if we can't fetch bridge strategy data
return undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could instead return the expected JSON object but all fields set to false?
This would be a more clear return type than undefined.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good point, that's how I initially had it but decided to give a nullable return type instead because:

I think it makes sense to keep each field nullable like you said if we decide to support partial fetching of bridge strategy data, where none, some, or all of the fields can nullable. Let me know what you think though.

Copy link
Contributor

@NikolasHaimerl NikolasHaimerl Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we show this behaviour in the JS doc string of the function? As a user of this function it is not immediately clear what undefined means here. Undefined can mean anything if the function does not explicitly state its meaning. And then we have to be careful, that the function can never return undefined at any other point of the function as it now has a distinct meaning.

I'll leave the decision up to you. Usually, I would argue it makes sense to make things either intuitive/the code is documentation enough or specify the behaviour as stringently as possible to avoid ambiguity.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I can add some docs. My reasoning is that each of the fields individually should never be undefined. We either have the bridge strategy data or we don't - undefined just means that we don't.

If we make all the fields in the type nullable, the structure technically feels wrong. Another option is to take the undefined out of the type and keep it in the function return type.

const depositAmountUsd = parseFloat(
ethers.utils.formatUnits(amountInInputTokenDecimals, inputToken.decimals)
);
const isInThreshold = depositAmountUsd <= 10_000; // 10K USD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we want to consider extracted hardcoded variables into a single place. This would help us later to make changes or understand why certain thresholds were set.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, will amend.

@ashwinrava ashwinrava changed the base branch from feat/mint-burn-bridge-routing to master October 9, 2025 15:17
@ashwinrava ashwinrava force-pushed the ashwin/acx-4481-implement-acrossnative-routing-logic branch from 671465d to 4493018 Compare October 9, 2025 15:28
Copy link
Contributor

@dohaki dohaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice this looks good!

@ashwinrava ashwinrava merged commit cc48ab3 into master Oct 13, 2025
10 checks passed
ashwinrava added a commit that referenced this pull request Oct 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants